/*
    This file is part of p4f
    Copyright (C) 2009  Sebastian Lezama

    p4f is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as 
    published by the Free Software Foundation, either version 3 of the 
    License, or (at your option) any later version.

    p4f is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
*/

#include <ftp.h>
#include <string>
#include <fstream>

FTPClient::FTPClient()
{   
    isConnected = false;                   
}


FTPClient::~FTPClient()
{
    if (isConnected)
        SendCommand("QUIT",RECV);                   
}


bool FTPClient::connect(std::string host, int port, std::string user, std::string pass)
{
//Connecting to host
    if (!s.connect(host,port))
       return false;

//Login		
    if (SendCommand(RECV,RECV).substr(0,3) == "220")
    {
	if (SendCommand("USER",user).substr(0,3) == "331")
        {
	    if (SendCommand("PASS",pass).substr(0,3) == "230")
            {
		isConnected = true;
		return true;
	    }
	}
	else if(response.substr(0,3) == "230")
        {
	    isConnected = true;
	    return true;
        }
    }
    error = response;
    return false;
}


bool FTPClient::UploadFile(std::string localpath, std::string remotepath)
{
    std::string IpPort;

    if (!isConnected)
    {
        error = "Not connected";
        return false;
    }   
  
    std::ifstream inFile(localpath.c_str(),std::ios::binary);
 
    if (!inFile)
    {
        error = "Open file failed";
        return false;            
    }
 
//Initialise pasive connection with the server
    if (SendCommand("PASV",RECV).substr(0,3) == "227")
    {
        IpPort = response;

        if (SendCommand("TYPE","I").substr(0,3) == "200")
//Send file        
            if (SendFile(inFile,remotepath,IpPort))
            
               if (SendCommand(RECV,RECV).substr(0,3) == "226")
                  return true;
    }

    return false;
}


bool FTPClient::DownloadFile(std::string remotepath, std::string localpath)
{
    std::string IpPort;

    if (!isConnected)
    {
        error = "Not connected";
        return false;
    }   
  
    std::ofstream outFile(localpath.c_str(),std::ios::binary);
 
    if (!outFile)
    {
        error = "Open file failed";
        return false;            
    }
 
//Initialise pasive connection with the server
    if (SendCommand("PASV",RECV).substr(0,3) == "227")
    {
        IpPort = response;

        if (SendCommand("TYPE","I").substr(0,3) == "200")
//Get file        
            if (GetFile(outFile,remotepath,IpPort))
            
               if (SendCommand(RECV,RECV).substr(0,3) == "226")
                  return true;
    }

    return false;
}


std::string FTPClient::SendCommand(std::string command, std::string value)
{
    static char dato[RECVBUFFER];
    static int  bytes;
    
    if (command != RECV)
        s.send(command + " " + value + "\r\n");
    
    if ((bytes = s.recv(dato,RECVBUFFER)) > 0)
    {
        return response.assign(dato,bytes);
    }   
    else
        return response.erase();   
}


void FTPClient::GetIPandPort(const std::string &data, std::string &ip, int &port)
{
 
    std::size_t leftp, rigthp, midp;
    std::string IpPort;
    int i = 0;
 
    leftp = data.find('(');
    rigthp = data.find(')');

//Get ip and port
    IpPort = data.substr(leftp+1,rigthp - leftp - 1);

//Find positions 
    for(i = 0, rigthp = IpPort.length();i < 2;i++)
    {  
        leftp = IpPort.rfind(',',rigthp);
        if (i == 0)
            midp = leftp;
        rigthp = leftp - 1;  
    }  

//First part of the port
    port = atoi(IpPort.substr(leftp+1, midp - leftp - 1).c_str()) * 256;
 
//Second part of the port
    port += atoi(IpPort.substr(midp+1, IpPort.length() - midp).c_str());

//IP
    ip = IpPort.substr(0,leftp);

//Replace "," by "." 
    for(i = 0, rigthp = 0;i < 3;i++)
    {
        leftp = ip.find(',',rigthp);    
        ip.replace(leftp,1,".");  
    } 
}


bool FTPClient::SendFile(std::ifstream &inFile, std::string &remotepath, std::string &IpPort)
{
    std::string ip;
    char buffer[READBUFFER];
    int port;

    TcpSocket NewSock;
 
    GetIPandPort(IpPort,ip,port);

    if (!NewSock.connect(ip,port))
        return false;     
 
    if (SendCommand("STOR",remotepath).substr(0,3) == "150")
    {
//Transfer file                                    
        while(!inFile.eof())
        {                  
            inFile.read(buffer,READBUFFER);
            NewSock.send(buffer,inFile.gcount());
        }
        inFile.close();
        return true;    
    }
    return false;       
}


bool FTPClient::GetFile(std::ofstream &outFile, std::string &remotepath, std::string &IpPort)
{
    std::string ip;
    char buffer[READBUFFER];
    int port, bytes, btot = 0, size;

    TcpSocket NewSock;
 
    GetIPandPort(IpPort,ip,port);

    if (!NewSock.connect(ip,port))
        return false;     
 
    if (SendCommand("RETR",remotepath).substr(0,3) == "150")
    {
        size = atoi(response.substr(response.find('(') + 1,response.find(' ') - 1).c_str());                                            
//Get file                                    
        while((bytes = NewSock.recv(buffer,READBUFFER)) > 0)
        {   
            if ((btot+= bytes) == size)
                bytes = bytes - 1;
                   
            outFile.write(buffer,bytes);
        }
        outFile.close();
        return true;    
    }
    return false;       
}
